#include <sdk.h>
#include "gdb_driver.h"
#include "gdb_commands.h"
#include "debuggeroptionsdlg.h"
#include "debuggerstate.h"
#include <M_Code_debugger_interfaces.h>
#include <manager.h>
#include <macrosmanager.h>
#include <configmanager.h>
#include <globals.h>
#include <infowindow.h>

#define GDB_PROMPT _T("cb_gdb:")
#define FULL_GDB_PROMPT _T(">>>>>>") GDB_PROMPT

static wxRegEx reThreadSwitch( _T( "^\\[Switching to thread .*\\]#0[ \t]+(0x[A-Fa-f0-9]+) in (.*) from (.*)" ) );
static wxRegEx reThreadSwitch2( _T( "^\\[Switching to thread .*\\]#0[ \t]+(0x[A-Fa-f0-9]+) in (.*) from (.*):([0-9]+)" ) );
static wxRegEx reBreak( _T( "\032*([A-Za-z]*[:]*)([^:]+):([0-9]+):[0-9]+:[begmidl]+:(0x[0-9A-Fa-f]+)" ) );
static wxRegEx reBreak2( _T( "^(0x[A-Fa-f0-9]+) in (.*) from (.*)" ) );
static wxRegEx reBreak3( _T( "^(0x[A-Fa-f0-9]+) in (.*)" ) );
static wxRegEx reCatchThrow( _T( "^Catchpoint ([0-9]+) \\(exception thrown\\), (0x[0-9a-f]+) in (.+) from (.+)$" ) );
static wxRegEx reCatchThrowNoFile( _T( "^Catchpoint ([0-9]+) \\(exception thrown\\), (0x[0-9a-f]+) in (.+)$" ) );
static wxRegEx rePendingFound( _T( "^Pending[ \t]+breakpoint[ \t]+[\"]+([^:]+):([0-9]+)\".*" ) );
static wxRegEx rePendingFound1( _T( "^Breakpoint[ \t]+([0-9]+),.*" ) );
static wxRegEx reTempBreakFound( _T( "^[Tt]emporary[ \t]breakpoint[ \t]([0-9]+),.*" ) );
static wxRegEx reChildPid1( _T( "Thread[ \t]+[xA-Fa-f0-9-]+[ \t]+\\(LWP ([0-9]+)\\)]" ) );
static wxRegEx reChildPid2( _T( "\\[New [tT]hread[ \t]+[0-9]+\\.[xA-Fa-f0-9-]+\\]" ) );
static wxRegEx reAttachedChildPid( _T( "Attaching to process ([0-9]+)" ) );
static wxRegEx reInferiorExited( _T( "^\\[Inferior[ \\t].+[ \\t]exited normally\\]$" ), wxRE_EXTENDED );
static wxRegEx reInferiorExitedWithCode( _T( "^\\[[Ii]nferior[ \\t].+[ \\t]exited[ \\t]with[ \\t]code[ \\t]([0-9]+)\\]$" ), wxRE_EXTENDED );

GDB_driver::GDB_driver( DebuggerGDB* plugin ) :
  DebuggerDriver( plugin ),
  m_CygwinPresent( false ),
  m_BreakOnEntry( false ),
  m_ManualBreakOnEntry( false ),
  m_IsStarted( false ),
  m_GDBVersionMajor( 0 ),
  m_GDBVersionMinor( 0 ),
  m_attachedToProcess( false ),
  m_catchThrowIndex( -1 ) {
  m_needsUpdate = false;
  m_forceUpdate = false;
}

GDB_driver::~GDB_driver() {
}

wxString GDB_driver::GetCommandLine( const wxString& debugger, const wxString& debuggee, const wxString &userArguments ) {
  wxString cmd;
  cmd << debugger;
  if( m_pDBG->GetActiveConfigEx().GetFlag( DebuggerConfiguration::DisableInit ) ) {
    cmd << _T( " -nx" );
  }
  cmd << _T( " -fullname" );
  cmd << _T( " -quiet" );
  cmd << _T( " " ) << userArguments;
  cmd << _T( " -args " ) << debuggee;
  return cmd;
}

wxString GDB_driver::GetCommandLine( const wxString& debugger, M_Code_unused int pid, const wxString &userArguments ) {
  wxString cmd;
  cmd << debugger;
  if( m_pDBG->GetActiveConfigEx().GetFlag( DebuggerConfiguration::DisableInit ) ) {
    cmd << _T( " -nx" );
  }
  cmd << _T( " -fullname" );
  cmd << _T( " -quiet" );
  cmd << _T( " " ) << userArguments;
  return cmd;
}

void GDB_driver::SetTarget( ProjectBuildTarget* target ) {
  m_pTarget = target;
}

void GDB_driver::Prepare( bool isConsole, int printElements ) {
  QueueCommand( new DebuggerCmd( this, wxString( _T( "set prompt " ) ) + FULL_GDB_PROMPT ) );
  QueueCommand( new DebuggerCmd( this, _T( "show version" ) ) );
  QueueCommand( new DebuggerCmd( this, _T( "set confirm off" ) ) );
  QueueCommand( new DebuggerCmd( this, _T( "set width 0" ) ) );
  QueueCommand( new DebuggerCmd( this, _T( "set height 0" ) ) );
  QueueCommand( new DebuggerCmd( this, _T( "set breakpoint pending on" ) ) );
  QueueCommand( new DebuggerCmd( this, _T( "set print asm-demangle on" ) ) );
  QueueCommand( new DebuggerCmd( this, _T( "set unwindonsignal on" ) ) );
  QueueCommand( new DebuggerCmd( this, F( _T( "set print elements %d" ), printElements ) ) );
  flavour = m_pDBG->GetActiveConfigEx().GetDisassemblyFlavorCommand();
  QueueCommand( new DebuggerCmd( this, flavour ) );
  if( m_pDBG->GetActiveConfigEx().GetFlag( DebuggerConfiguration::CatchExceptions ) ) {
    m_catchThrowIndex = -1;
    QueueCommand( new GdbCmd_SetCatch( this, _T( "throw" ), &m_catchThrowIndex ) );
  }
  wxString init = m_pDBG->GetActiveConfigEx().GetInitCommands();
  MacrosManager *macrosManager = Manager::Get()->GetMacrosManager();
  macrosManager->ReplaceMacros( init );
  if( !init.empty() ) {
    QueueCommand( new DebuggerCmd( this, init ) );
  }
  for( unsigned int i = 0; i < m_Dirs.GetCount(); ++i ) {
    QueueCommand( new GdbCmd_AddSourceDir( this, m_Dirs[i] ) );
  }
  if( !m_Args.IsEmpty() ) {
    QueueCommand( new DebuggerCmd( this, _T( "set args " ) + m_Args ) );
  }
  RemoteDebugging* rd = GetRemoteDebuggingInfo();
  if( rd ) {
    if( !rd->additionalCmdsBefore.IsEmpty() ) {
      wxArrayString initCmds = GetArrayFromString( rd->additionalCmdsBefore, _T( '\n' ) );
      for( unsigned int i = 0; i < initCmds.GetCount(); ++i ) {
        macrosManager->ReplaceMacros( initCmds[i] );
        QueueCommand( new DebuggerCmd( this, initCmds[i] ) );
      }
    }
    if( !rd->additionalShellCmdsBefore.IsEmpty() ) {
      wxArrayString initCmds = GetArrayFromString( rd->additionalShellCmdsBefore, _T( '\n' ) );
      for( unsigned int i = 0; i < initCmds.GetCount(); ++i ) {
        macrosManager->ReplaceMacros( initCmds[i] );
        QueueCommand( new DebuggerCmd( this, _T( "shell " ) + initCmds[i] ) );
      }
    }
  }
  if( rd && rd->IsOk() ) {
    if( rd->connType == RemoteDebugging::Serial ) {
      QueueCommand( new GdbCmd_RemoteBaud( this, rd->serialBaud ) );
    }
    QueueCommand( new GdbCmd_RemoteTarget( this, rd ) );
  }
  if( rd ) {
    if( !rd->additionalCmds.IsEmpty() ) {
      wxArrayString initCmds = GetArrayFromString( rd->additionalCmds, _T( '\n' ) );
      for( unsigned int i = 0; i < initCmds.GetCount(); ++i ) {
        macrosManager->ReplaceMacros( initCmds[i] );
        QueueCommand( new DebuggerCmd( this, initCmds[i] ) );
      }
    }
    if( !rd->additionalShellCmdsAfter.IsEmpty() ) {
      wxArrayString initCmds = GetArrayFromString( rd->additionalShellCmdsAfter, _T( '\n' ) );
      for( unsigned int i = 0; i < initCmds.GetCount(); ++i ) {
        macrosManager->ReplaceMacros( initCmds[i] );
        QueueCommand( new DebuggerCmd( this, _T( "shell " ) + initCmds[i] ) );
      }
    }
  }
}

RemoteDebugging* GDB_driver::GetRemoteDebuggingInfo() {
  m_MergedRDInfo = m_pDBG->GetRemoteDebuggingMap()[0];
  RemoteDebuggingMap::iterator it = m_pDBG->GetRemoteDebuggingMap().find( m_pTarget );
  if( it != m_pDBG->GetRemoteDebuggingMap().end() ) {
    m_MergedRDInfo.MergeWith( it->second );
  }
  return &m_MergedRDInfo;
}

void GDB_driver::DetectCygwinMount( void ) {}
void GDB_driver::CorrectCygwinPath( M_Code_unused wxString& path ) {}
wxString GDB_driver::GetDisassemblyFlavour( void ) {
  return flavour;
}

void GDB_driver::Start( bool breakOnEntry ) {
  m_attachedToProcess = false;
  ResetCursor();
  GdbCmd_DisassemblyInit::Clear();
  if( Manager::Get()->GetDebuggerManager()->UpdateDisassembly() ) {
    cbDisassemblyDlg *disassembly_dialog = Manager::Get()->GetDebuggerManager()->GetDisassemblyDialog();
    disassembly_dialog->Clear( cbStackFrame() );
  }
  RemoteDebugging* rd = GetRemoteDebuggingInfo();
  bool remoteDebugging = rd && rd->IsOk();
  m_BreakOnEntry = breakOnEntry && !remoteDebugging;
  if( !m_pDBG->GetActiveConfigEx().GetFlag( DebuggerConfiguration::DoNotRun ) ) {
    m_ManualBreakOnEntry = !remoteDebugging;
    if( breakOnEntry ) {
      QueueCommand( new GdbCmd_Start( this, remoteDebugging ? _T( "continue" ) : _T( "start" ) ) );
    } else {
      m_ManualBreakOnEntry = false;
      QueueCommand( new GdbCmd_Start( this, remoteDebugging ? _T( "continue" ) : _T( "run" ) ) );
    }
    m_IsStarted = true;
  }
}

void GDB_driver::Stop() {
  ResetCursor();
  if( m_pDBG->IsAttachedToProcess() ) {
    QueueCommand( new DebuggerCmd( this, _T( "kill" ) ) );
  }
  QueueCommand( new DebuggerCmd( this, _T( "quit" ) ) );
  m_IsStarted = false;
  m_attachedToProcess = false;
}

void GDB_driver::Continue() {
  ResetCursor();
  if( m_IsStarted ) {
    QueueCommand( new GdbCmd_Continue( this ) );
  } else {
    RemoteDebugging* rd = GetRemoteDebuggingInfo();
    bool remoteDebugging = rd && rd->IsOk();
    if( remoteDebugging ) {
      QueueCommand( new GdbCmd_Continue( this ) );
    } else
    { QueueCommand( new GdbCmd_Start( this, m_ManualBreakOnEntry ? _T( "start" ) : _T( "run" ) ) ); }
    m_ManualBreakOnEntry = false;
    m_IsStarted = true;
    m_attachedToProcess = false;
  }
}

void GDB_driver::Step() {
  ResetCursor();
  QueueCommand( new DebuggerContinueBaseCmd( this, _T( "next" ) ) );
}

void GDB_driver::StepInstruction() {
  ResetCursor();
  QueueCommand( new GdbCmd_StepInstruction( this ) );
}

void GDB_driver::StepIntoInstruction() {
  ResetCursor();
  QueueCommand( new GdbCmd_StepIntoInstruction( this ) );
}

void GDB_driver::StepIn() {
  ResetCursor();
  QueueCommand( new DebuggerContinueBaseCmd( this, _T( "step" ) ) );
}

void GDB_driver::StepOut() {
  ResetCursor();
  QueueCommand( new DebuggerContinueBaseCmd( this, _T( "finish" ) ) );
}

void GDB_driver::SetNextStatement( const wxString& filename, int line ) {
  ResetCursor();
  QueueCommand( new DebuggerCmd( this, F( _T( "tbreak %s:%d" ), filename.c_str(), line ) ) );
  QueueCommand( new DebuggerContinueBaseCmd( this, F( _T( "jump %s:%d" ), filename.c_str(), line ) ) );
}

void GDB_driver::Backtrace() {
  QueueCommand( new GdbCmd_Backtrace( this ) );
}

void GDB_driver::Disassemble() {
  QueueCommand( new GdbCmd_DisassemblyInit( this ) );
}

void GDB_driver::CPURegisters() {
  QueueCommand( new GdbCmd_InfoRegisters( this ) );
}

void GDB_driver::SwitchToFrame( size_t number ) {
  ResetCursor();
  QueueCommand( new DebuggerCmd( this, wxString( _T( "frame " ) ) << number ) );
}

void GDB_driver::SetVarValue( const wxString& var, const wxString& value ) {
  const wxString &cleanValue = CleanStringValue( value );
  QueueCommand( new DebuggerCmd( this, F( _T( "set variable %s=%s" ), var.c_str(), cleanValue.c_str() ) ) );
}

void GDB_driver::MemoryDump() {
  QueueCommand( new GdbCmd_ExamineMemory( this ) );
}

void GDB_driver::RunningThreads() {
  if( Manager::Get()->GetDebuggerManager()->UpdateThreads() ) {
    QueueCommand( new GdbCmd_Threads( this ) );
  }
}

void GDB_driver::InfoFrame() {
  QueueCommand( new DebuggerInfoCmd( this, _T( "info frame" ), _T( "Selected frame" ) ) );
}

void GDB_driver::InfoDLL() {
  QueueCommand( new DebuggerInfoCmd( this, _T( "info sharedlibrary" ), _T( "Loaded libraries" ) ) );
}

void GDB_driver::InfoFiles() {
  QueueCommand( new DebuggerInfoCmd( this, _T( "info files" ), _T( "Files and targets" ) ) );
}

void GDB_driver::InfoFPU() {
  QueueCommand( new DebuggerInfoCmd( this, _T( "info float" ), _T( "Floating point unit" ) ) );
}

void GDB_driver::InfoSignals() {
  QueueCommand( new DebuggerInfoCmd( this, _T( "info signals" ), _T( "Signals handling" ) ) );
}

void GDB_driver::EnableCatchingThrow( bool enable ) {
  if( enable ) {
    QueueCommand( new GdbCmd_SetCatch( this, _T( "throw" ), &m_catchThrowIndex ) );
  } else if( m_catchThrowIndex != -1 ) {
    QueueCommand( new DebuggerCmd( this, F( _T( "delete %d" ), m_catchThrowIndex ) ) );
    m_catchThrowIndex = -1;
  }
}

void GDB_driver::SwitchThread( size_t threadIndex ) {
  ResetCursor();
  QueueCommand( new DebuggerCmd( this, F( _T( "thread %lu" ), static_cast<unsigned long>( threadIndex ) ) ) );
  if( Manager::Get()->GetDebuggerManager()->UpdateBacktrace() ) {
    QueueCommand( new GdbCmd_Backtrace( this ) );
  }
}

void GDB_driver::AddBreakpoint( cb::shared_ptr<DebuggerBreakpoint> bp ) {
  if( bp->type == DebuggerBreakpoint::bptData ) {
    QueueCommand( new GdbCmd_AddDataBreakpoint( this, bp ) );
  } else {
    if( bp->func.IsEmpty() && !bp->lineText.IsEmpty() ) {
      wxRegEx reCtorDtor( _T( "([0-9A-z_]+)::([~]?)([0-9A-z_]+)[ \t\(]*" ) );
      if( reCtorDtor.Matches( bp->lineText ) ) {
        wxString strBase = reCtorDtor.GetMatch( bp->lineText, 1 );
        wxString strDtor = reCtorDtor.GetMatch( bp->lineText, 2 );
        wxString strMethod = reCtorDtor.GetMatch( bp->lineText, 3 );
        if( strBase.IsSameAs( strMethod ) ) {
          bp->func = strBase;
          bp->func << _T( "::" );
          bp->func << strDtor;
          bp->func << strMethod;
          NotifyCursorChanged();
        }
      }
    }
    QueueCommand( new GdbCmd_AddBreakpoint( this, bp ) );
  }
}

void GDB_driver::RemoveBreakpoint( cb::shared_ptr<DebuggerBreakpoint> bp ) {
  if( bp && bp->index != -1 ) {
    QueueCommand( new GdbCmd_RemoveBreakpoint( this, bp ) );
  }
}

void GDB_driver::EvaluateSymbol( const wxString& symbol, const wxRect& tipRect ) {
  QueueCommand( new GdbCmd_FindTooltipType( this, symbol, tipRect ) );
}

void GDB_driver::UpdateWatches( cb::shared_ptr<GDBWatch> localsWatch, cb::shared_ptr<GDBWatch> funcArgsWatch,
                                WatchesContainer &watches ) {
  bool updateWatches = false;
  if( localsWatch && localsWatch->IsAutoUpdateEnabled() ) {
    QueueCommand( new GdbCmd_LocalsFuncArgs( this, localsWatch, true ) );
    updateWatches = true;
  }
  if( funcArgsWatch && funcArgsWatch->IsAutoUpdateEnabled() ) {
    QueueCommand( new GdbCmd_LocalsFuncArgs( this, funcArgsWatch, false ) );
    updateWatches = true;
  }
  for( WatchesContainer::iterator it = watches.begin(); it != watches.end(); ++it ) {
    WatchesContainer::reference watch = *it;
    if( watch->IsAutoUpdateEnabled() ) {
      QueueCommand( new GdbCmd_FindWatchType( this, watch ) );
      updateWatches = true;
    }
  }
  if( updateWatches ) {
    QueueCommand( new DbgCmd_UpdateWatchesTree( this ) );
  }
}

void GDB_driver::UpdateWatch( const cb::shared_ptr<GDBWatch> &watch ) {
  QueueCommand( new GdbCmd_FindWatchType( this, watch ) );
  QueueCommand( new DbgCmd_UpdateWatchesTree( this ) );
}

void GDB_driver::UpdateWatchLocalsArgs( cb::shared_ptr<GDBWatch> const &watch, bool locals ) {
  QueueCommand( new GdbCmd_LocalsFuncArgs( this, watch, locals ) );
  QueueCommand( new DbgCmd_UpdateWatchesTree( this ) );
}

void GDB_driver::Attach( int pid ) {
  m_IsStarted = true;
  m_attachedToProcess = true;
  SetChildPID( pid );
  QueueCommand( new GdbCmd_AttachToProcess( this, pid ) );
}

void GDB_driver::Detach() {
  QueueCommand( new GdbCmd_Detach( this ) );
}

void GDB_driver::ParseOutput( const wxString& output ) {
  m_Cursor.changed = false;
  if( m_ChildPID == 0 ) {
    if( reChildPid1.Matches( output ) ) {
      wxString pidStr = reChildPid1.GetMatch( output, 1 );
      long pid = 0;
      pidStr.ToLong( &pid );
      SetChildPID( pid );
      m_pDBG->Log( F( _T( "Child process PID: %ld" ), pid ) );
    }
  }
  if( output.StartsWith( _T( "gdb: " ) )
      || output.StartsWith( _T( "warning: " ) )
      || output.StartsWith( _T( "Warning: " ) )
      || output.StartsWith( _T( "ContinueDebugEvent " ) ) ) {
    return;
  }
  static wxString buffer;
  buffer << output << _T( '\n' );
  m_pDBG->DebugLog( output );
  int idx = buffer.First( GDB_PROMPT );
  const bool foundPrompt = ( idx != wxNOT_FOUND );
  if( !foundPrompt ) {
    return;
  }
  m_QueueBusy = false;
  int changeFrameAddr = 0 ;
  DebuggerCmd* cmd = CurrentCommand();
  if( cmd ) {
    RemoveTopCommand( false );
    buffer.Remove( idx );
    int cnt = 6;
    while( !buffer.empty() && buffer.Last() == _T( '>' ) && cnt-- ) {
      buffer.RemoveLast();
    }
    if( !buffer.empty() && buffer.Last() == _T( '\n' ) ) {
      buffer.RemoveLast();
    }
    cmd->ParseOutput( buffer.Left( idx ) );
    GdbCmd_ChangeFrame *changeFrameCmd = dynamic_cast<GdbCmd_ChangeFrame*>( cmd );
    if( changeFrameCmd ) {
      changeFrameAddr = changeFrameCmd->AddrChgMode();
    }
    delete cmd;
    RunQueue();
  }
  m_needsUpdate = false;
  m_forceUpdate = false;
  wxArrayString lines = GetArrayFromString( buffer, _T( '\n' ) );
  for( unsigned int i = 0; i < lines.GetCount(); ++i ) {
    if( lines[i].StartsWith( _T( "GNU gdb" ) ) ) {
      m_pDBG->Log( _T( "Debugger name and version: " ) + lines[i] );
      wxRegEx re( _T( "([0-9.]+)" ) );
      if( !re.Matches( lines[i] ) ) {
        m_pDBG->Log( _T( "Unable to determine the version of gdb" ) );
        break;
      }
      wxString major = re.GetMatch( lines[i], 0 );
      wxString minor = major;
      major = major.BeforeFirst( _T( '.' ) );
      minor = minor.AfterFirst( _T( '.' ) );
      minor = minor.BeforeFirst( _T( '.' ) );
      major.ToLong( &m_GDBVersionMajor );
      minor.ToLong( &m_GDBVersionMinor );
      break;
    } else if( lines[i].StartsWith( _T( "Error creating process" ) )
               || lines[i].StartsWith( _T( "Program exited" ) )
               || lines[i].StartsWith( _T( "Program terminated with signal" ) )
               || lines[i].StartsWith( _T( "During startup program exited" ) )
               || lines[i].Contains( _T( "program is not being run" ) )
               || lines[i].Contains( _T( "Target detached" ) )
               || reInferiorExited.Matches( lines[i] )
               || reInferiorExitedWithCode.Matches( lines[i] ) ) {
      m_pDBG->Log( lines[i] );
      m_ProgramIsStopped = true;
      QueueCommand( new DebuggerCmd( this, _T( "quit" ) ) );
      m_IsStarted = false;
    } else if( lines[i].Contains( _T( "(no debugging symbols found)" ) ) ) {
      m_pDBG->Log( lines[i] );
    } else if( lines[i].StartsWith( _T( "Program received signal SIG" ) ) ) {
      m_ProgramIsStopped = true;
      m_QueueBusy = false;
      if( lines[i].StartsWith( _T( "Program received signal SIGINT" ) )
          || lines[i].StartsWith( _T( "Program received signal SIGTRAP" ) )
          || lines[i].StartsWith( _T( "Program received signal SIGSTOP" ) ) ) {
        Log( lines[i] );
      } else {
        Log( lines[i] );
        m_pDBG->BringCBToFront();
        if( Manager::Get()->GetDebuggerManager()->ShowBacktraceDialog() ) {
          m_forceUpdate = true;
        }
        InfoWindow::Tip( _T( "\n\n" ) + lines[i] + _T( "\n\n" ), _T( "Signal received" ) );
        m_needsUpdate = true;
      }
    } else if( lines[i].StartsWith( _T( "Error " ) )
               || lines[i].StartsWith( _T( "No such" ) )
               || lines[i].StartsWith( _T( "Cannot evaluate" ) ) ) {
      m_pDBG->Log( lines[i] );
    } else if( ( lines[i].StartsWith( _T( "Cannot find bounds of current function" ) ) )
               || ( lines[i].StartsWith( _T( "No stack" ) ) ) ) {
      m_pDBG->Log( lines[i] );
      m_ProgramIsStopped = true;
    } else if( lines[i].StartsWith( _T( "Pending breakpoint " ) ) ) {
      m_pDBG->Log( lines[i] );
      wxString bpstr = lines[i];
      if( rePendingFound.Matches( bpstr ) ) {
        wxString newbpstr = lines[++i];
        while( i < lines.GetCount() - 1 && newbpstr.StartsWith( _T( "[" ) ) ) {
          newbpstr = lines[++i];
        }
        if( rePendingFound1.Matches( newbpstr ) ) {
          wxString file;
          wxString lineStr;
          file = rePendingFound.GetMatch( bpstr, 1 );
          lineStr = rePendingFound.GetMatch( bpstr, 2 );
          file = UnixFilename( file );
          long line;
          lineStr.ToLong( &line );
          DebuggerState& state = m_pDBG->GetState();
          int bpindex = state.HasBreakpoint( file, line - 1, false );
          cb::shared_ptr<DebuggerBreakpoint> bp = state.GetBreakpoint( bpindex );
          if( bp ) {
            long index;
            wxString indexStr = rePendingFound1.GetMatch( newbpstr, 1 );
            indexStr.ToLong( &index );
            bp->index = index;
          }
        }
      }
    } else if( lines[i].StartsWith( _T( "Breakpoint " ) ) ) {
      if( rePendingFound1.Matches( lines[i] ) ) {
        long index;
        rePendingFound1.GetMatch( lines[i], 1 ).ToLong( &index );
        DebuggerState& state = m_pDBG->GetState();
        cb::shared_ptr<DebuggerBreakpoint> bp = state.GetBreakpointByNumber( index );
        if( bp && bp->wantsCondition ) {
          bp->wantsCondition = false;
          QueueCommand( new GdbCmd_AddBreakpointCondition( this, bp ) );
          m_needsUpdate = true;
        }
      }
    } else if( lines[i].StartsWith( _T( "Temporary breakpoint" ) ) ) {
      if( reTempBreakFound.Matches( lines[i] ) ) {
        long index;
        reTempBreakFound.GetMatch( lines[i], 1 ).ToLong( &index );
        DebuggerState& state = m_pDBG->GetState();
        cb::shared_ptr<DebuggerBreakpoint> bp = state.GetBreakpointByNumber( index );
        state.RemoveBreakpoint( bp, false );
        Manager::Get()->GetDebuggerManager()->GetBreakpointDialog()->Reload();
      }
    } else if( lines[i].StartsWith( g_EscapeChar ) ) {
      HandleMainBreakPoint( reBreak, lines[i] );
    } else {
      wxRegEx* re = 0;
      if( reBreak2.Matches( lines[i] ) ) {
        re = &reBreak2;
      } else if( reThreadSwitch.Matches( lines[i] ) ) {
        re = &reThreadSwitch;
      }
      if( re ) {
        m_Cursor.file = re->GetMatch( lines[i], 3 );
        m_Cursor.function = re->GetMatch( lines[i], 2 );
        wxString lineStr = _T( "" );
        m_Cursor.address = re->GetMatch( lines[i], 1 );
        m_Cursor.line = -1;
        m_Cursor.changed = true;
        m_needsUpdate = true;
      } else if( reThreadSwitch2.Matches( lines[i] ) ) {
        m_Cursor.file = reThreadSwitch2.GetMatch( lines[i], 3 );
        m_Cursor.function = reThreadSwitch2.GetMatch( lines[i], 2 );
        wxString lineStr = reThreadSwitch2.GetMatch( lines[i], 4 );
        m_Cursor.address = reThreadSwitch2.GetMatch( lines[i], 1 );
        m_Cursor.line = -1;
        m_Cursor.changed = true;
        m_needsUpdate = true;
      } else if( reBreak3.Matches( lines[i] ) ) {
        m_Cursor.file = _T( "" );
        m_Cursor.function = reBreak3.GetMatch( lines[i], 2 );
        m_Cursor.address = reBreak3.GetMatch( lines[i], 1 );
        m_Cursor.line = -1;
        m_Cursor.changed = true;
        m_needsUpdate = true;
      } else if( reCatchThrow.Matches( lines[i] ) ) {
        m_Cursor.file = reCatchThrow.GetMatch( lines[i], 4 );
        m_Cursor.function = reCatchThrow.GetMatch( lines[i], 3 );
        m_Cursor.address = reCatchThrow.GetMatch( lines[i], 2 );
        m_Cursor.line = -1;
        m_Cursor.changed = true;
        m_needsUpdate = true;
      } else if( reCatchThrowNoFile.Matches( lines[i] ) ) {
        m_Cursor.file = wxEmptyString;
        m_Cursor.function = reCatchThrowNoFile.GetMatch( lines[i], 3 );
        m_Cursor.address = reCatchThrowNoFile.GetMatch( lines[i], 2 );
        m_Cursor.line = -1;
        m_Cursor.changed = true;
        m_needsUpdate = true;
      }
    }
  }
  buffer.Clear();
  if( foundPrompt && m_DCmds.empty() && !m_ProgramIsStopped && !m_Cursor.changed ) {
    QueueCommand( new GdbCmd_FindCursor( this ) );
    m_ProgramIsStopped = true;
  }
  if( m_needsUpdate ) {
    if( 1 == changeFrameAddr ) {
      m_Cursor.address.clear();
    }
    if( m_Cursor.changed ) {
      m_ProgramIsStopped = true;
      m_QueueBusy = false;
    }
    if( m_Cursor.changed || m_forceUpdate ) {
      NotifyCursorChanged();
    }
  }
  if( m_ProgramIsStopped ) {
    RunQueue();
  }
}


void GDB_driver::HandleMainBreakPoint( const wxRegEx& reBreak_in, wxString line ) {
  if( reBreak_in.Matches( line ) ) {
    if( m_ManualBreakOnEntry ) {
      QueueCommand( new GdbCmd_InfoProgram( this ), DebuggerDriver::High );
    }
    if( m_ManualBreakOnEntry && !m_BreakOnEntry ) {
      Continue();
    } else {
      m_ManualBreakOnEntry = false;
      wxString lineStr;
      m_Cursor.file = reBreak_in.GetMatch( line, 2 );
      lineStr = reBreak_in.GetMatch( line, 3 );
      m_Cursor.address = reBreak_in.GetMatch( line, 4 );
      lineStr.ToLong( &m_Cursor.line );
      m_Cursor.changed = true;
      m_needsUpdate = true;
    }
  } else {
    m_pDBG->Log( _T( "The program has stopped on a breakpoint but the breakpoint format is not recognized:" ) );
    m_pDBG->Log( line );
    m_Cursor.changed = true;
    m_needsUpdate = true;
  }
}
